from abc import ABCMeta

from ai.chatgpt.chat_gpt_holder import ChatGPTHolder
from constants.config import Config
from support.redis_support import RedisSupport
from loguru import logger
import re
import json


class Template(metaclass=ABCMeta):
    """
    客户端抽象类
    """

    def __init__(self):
        self.bot_holder = ChatGPTHolder()

    def receive_message(self, data):
        """
        接收消息
        :param data:
        :return:
        """

        # 消息预处理
        message_id, text = self.pre_receive(data)
        # 幂等
        if Template.receive_idempotent(self.bot_holder.session_id + "_" + message_id, Config.idempotent.replay_expire_time):
            return json.dumps({"code": 0, "msg": "success"})
        # 调用模板方法
        self.do_receive(data, text)
        return json.dumps({"code": 0, "msg": "success"})

    @staticmethod
    def receive_idempotent(redis_key, expire):
        """
        事件幂等
        :param redis_key:
        :param expire:
        :return:
        """

        if RedisSupport.redis().exists(redis_key):
            logger.warning("事件已处理，忽略")
            return True
        RedisSupport.redis().set(redis_key, 1, ex=expire)
        return False

    def pre_receive(self, data):
        """
        消息预处理
        :return:
        """

        return

    def format_response(self, response):
        """
        格式化回复
        :param response:
        :return:
        """

        if self.bot_holder.current_presets == '阴阳人':
            # 如果Response长度为2
            if len(response) == 2:
                response = response[:len(response) - 1]

        # 如果Response包含换行符
        if "\n" in response and "\\n" not in response:
            # 替换换行符
            response = response.replace("\n", "\\n")

        logger.info("format_response: %s" % response)
        return response

    def replay_message(self, data, gpt_response):
        """
        回复消息
        :param data:
        :param gpt_response:
        :return:
        """

        return

    def do_receive(self, data, text):
        """
        接收消息模板方法
        :param data:
        :param text:
        :return:
        """

        if text in Config.trigger.reset_command:
            # 重置会话
            self.reset(data)
        elif text in Config.trigger.get_token_total_command:
            # 获取token总数
            self.get_token_total(data)
        elif switch_preset_search := re.search(Config.preset.command, text):
            # 切换预设
            self.change_role(data, switch_preset_search)
        elif text in Config.trigger.get_preset_command:
            # 获取当前预设
            self.get_preset(data)
        else:
            # 调用OpenAI
            self.invoke_openai(data, text)
        pass

    def reset(self, data):
        """
        重置会话
        :param data:
        :return:
        """

        # 会话是否存在
        if self.bot_holder.session_id not in self.bot_holder.bot.conversation:
            self.replay_message(data, "当前群组无有效会话，请先发送消息")
            return

        # 是否满足重置条件
        user_id = data.get('event').get('sender').get("sender_id").get('user_id')
        if not self.is_reset_condition(user_id):
            self.do_reset()
            self.replay_message(data, "重置成功")
        else:
            self.replay_message(data, "重置指令已收到")

    def is_reset_condition(self, user_id):
        """
        是否满足重置条件，提供默认实现
        :param user_id: 投票用户ID
        :return: 投票人数 >= 群成员数量 * 重置阈值 + 1
        """

        # 累计重置指令数量
        redis_key = "reset_count_set_%s" % self.bot_holder.session_id
        # 保存到redis set中
        RedisSupport.redis().sadd(redis_key, user_id)
        # 获取set中元素数量
        reset_count = RedisSupport.redis().scard(redis_key)

        # 获取群成员数量
        chat_member_count = self.get_chat_member_count()

        # 重置阈值
        threshold = int(chat_member_count * Config.sessionHolder.reset_threshold) + 1
        logger.info("重置阈值：%s" % threshold)
        return reset_count >= threshold

    def do_reset(self):
        """
        执行重置
        :return:
        """

        self.bot_holder.bot.conversation[self.bot_holder.session_id] = []
        self.bot_holder.__conversation_keep_from = 0
        RedisSupport.redis().delete("reset_count_set_%s" % self.bot_holder.session_id)

    def get_chat_member_count(self):
        """
        获取群成员数量
        :return:
        """

        return 1

    def get_token_total(self, data):
        """
        获取token总数
        :param data:
        :return:
        """

        # 会话是否存在
        if self.bot_holder.session_id not in self.bot_holder.bot.conversation:
            self.replay_message(data, "当前群组无有效会话，请先发送消息")
            return

        token_number = self.bot_holder.bot.get_token_count(self.bot_holder.session_id)
        # 回复token数量
        self.replay_message(data, "当前token数量：%s" % token_number)

    def change_role(self, data, switch_preset_search):
        """
        切换预设
        :param data:
        :param switch_preset_search:
        :return:
        """

        model_name = switch_preset_search[1].strip()
        self.bot_holder.bot.system_prompt = self.bot_holder.preset_dict[model_name]

        self.do_reset()
        self.preset_ask(self.bot_holder.bot.system_prompt)
        self.bot_holder.current_presets = model_name
        self.invoke_openai(data, "")

    def preset_ask(self, text: str):
        """
        以预设的方式提问
        :param text:
        :return:
        """

        self.bot_holder.bot.conversation[self.bot_holder.session_id].append({"role": "system", "content": text})
        self.bot_holder.__conversation_keep_from = len(self.bot_holder.bot.conversation[self.bot_holder.session_id])

    def invoke_openai(self, data, text):
        """
        调用OpenAI
        :param data:
        :param text:
        :return:
        """

        response = self.bot_holder.ask(text)
        response = self.format_response(response)
        # 回复消息
        self.replay_message(data, response)

    def get_preset(self, data):
        """
        获取当前预设
        :param data:
        :return:
        """

        self.replay_message(data, "当前预设为：%s" % self.bot_holder.current_presets)
